/**
 *	TrueMusic - simple Java music player
 *
 *	TrueMusic is free software; you can redistribute it
 *	and/or modify it under the terms of the GNU General
 *	Public License as published by the Free Software
 *	Foundation; either version 2 of the License or (at
 *	your option) any later version.
 *
 *	This program is distributed in the hope that it will be
 *	useful but WITHOUT ANY WARRANTY; without even the
 *	implied warranty of the MERCHANTABILITY or FITNESS FOR
 *	A PARTICULAR PURPOSE. See the GNU General Public
 *	License for more details.
 *
 *	Copyright (c)2005-2007 Ryan Macnak <rmacnak@gmail.com>
 */

package com.servequake.truemusic;

import de.jarnbjo.ogg.*;
import de.jarnbjo.flac.*;

import java.io.*;
import java.util.*;
import javax.sound.sampled.*;

import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;

import org.kc7bfi.jflac.PCMProcessor;
import org.kc7bfi.jflac.FLACDecoder;
import org.kc7bfi.jflac.metadata.StreamInfo;
import org.kc7bfi.jflac.util.ByteData;

/**
 * Audio for Native FLAC (FLAC not in an Ogg Stream) using org.kc7bfi.jflac
 */
public class NativeFlacAudio implements Audio, Runnable, PCMProcessor
{
	String res = "";
	PlaybackListener l = null;

	String title = "";
	String artist = "";
	
	private static final int BUFFER_SIZE = 65536;
   

	/**
	 * Called to load an audio resource (file/stream); an Audio format is expected to have a no-arg constructor.
	 * @throw Exception if this format cannot play the audio at the given resource
	 */
	public void load(String resource, PlaybackListener l) throws Exception
	{
		this.res = resource; this.l = l;
	}

	private AudioFormat fmt;
    private DataLine.Info info;
    private SourceDataLine line;
	private Vector listeners = new Vector();
	
	
	
	/////////////
	
	
	PhysicalOggStream os = null;
	LogicalOggStream los = null;
	 long t0 = 0L;
	FlacStream vs;
	 int len=0;
	 int channels;
int sampleRate;
	 VorbisComment comments;

	  FloatControl vol; 

	  
        byte[] buffer=new byte[BUFFER_SIZE];
		 
	  SourceDataLine getOutputLine(int channels, int rate)
	{
		try
		{
			AudioFormat af = new AudioFormat( (float)rate, 16, channels, true, false );
			DataLine.Info info = new DataLine.Info(SourceDataLine.class, af, AudioSystem.NOT_SPECIFIED);

			if( !AudioSystem.isLineSupported(info) ) throw new RuntimeException("AudioLine not supported!");

			SourceDataLine outputLine = (SourceDataLine) AudioSystem.getLine(info);
			outputLine.open(af);
			outputLine.start(); //the magic line: this was the one missing that i couldn't get it to start :(

			vol = (FloatControl)outputLine.getControl(FloatControl.Type.MASTER_GAIN);

		
			return outputLine;
		}
		catch(LineUnavailableException e)
		{
			e.printStackTrace();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return null;
	}
	
	Thread thread;

	public void play()
	{
	try{
	        FileInputStream is = new FileInputStream(res);
        
        final FLACDecoder decoder = new FLACDecoder(is);
        decoder.addPCMProcessor(this);
         
            new Thread(){
				public void run(){try{decoder.decode();l.playbackCompleted();}catch(Exception e){e.printStackTrace();}}
				}.start();
         
        
		}catch(Exception e){e.printStackTrace();}
  //      line.drain();
 //       line.close();

	    //  We're going to clear out the list of listeners as well, so that everytime through
	    //  things are basically at the same starting point.
//	    listeners.clear();
		
		
		
//		 line = getOutputLine(channels, sampleRate);

	}
	
	public void processStreamInfo(StreamInfo streamInfo) {
        try {
            fmt = streamInfo.getAudioFormat();
            info = new DataLine.Info(SourceDataLine.class, fmt, AudioSystem.NOT_SPECIFIED);
            line = (SourceDataLine) AudioSystem.getLine(info);

			
			
	        //  Add the listeners to the line at this point, it's the only
	        //  way to get the events triggered.
	  //      int size = listeners.size();
	 //        for (int index = 0; index < size; index++)
	 //          line.addLineListener((LineListener) listeners.get(index));

            line.open(fmt, AudioSystem.NOT_SPECIFIED);
			vol = (FloatControl)line.getControl(FloatControl.Type.MASTER_GAIN);

			//for(Object o : line.getControls()) System.out.println(o);
	
            line.start();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
	
	    public void processPCM(ByteData pcm) {
		
				vol.setValue(l.getVolume());
        line.write(pcm.getData(), 0, pcm.getLen());
    }
    

	public void removeListener (LineListener listener)
	{
		listeners.removeElement(listener);
	}
	
	public void addListener (LineListener listener)
	{
		listeners.add(listener);
	}

	/**
	 * Called to pause or unpause playback (resources may be left open).
	 */
	public void pause(){}

	/**
	 * Called to stop playback (set to beginning and pause); all resources should be released (streams, decorders, threads, ...).
	 */
	public void stop(){thread = null;}




	public int getFrames(){return 0;}
	public void seek(int frame){}

	
	public void run()
	{
		try
		{
            // read pcm data from the vorbis channel and
            // write the data to the wav file
            while(thread==Thread.currentThread()) {
               int read=vs.readPcm(buffer, 0, buffer.length);
               for(int i=0; i<read; i+=2) {
               	// swap from big endian to little endian
                  byte tB=buffer[i];
                  buffer[i]=buffer[i+1];
                  buffer[i+1]=tB;
               }
              // outFile.write(buffer, 0, read);
			  
				vol.setValue(l.getVolume());
				
				line.write(buffer,0,read);
               len+=read;
            }
         }
         catch(EndOfOggStreamException e) {
            // not really an error, but we've
            // reached the end of the vorbis stream
            // and so exit the loop
         }
		catch(IOException e){e.printStackTrace();}
	}
	
		
	public boolean isPlaying(){return false;}

	public String getResource(){return res;}
	public String getTitle(){if(title=="") return new File(res).getName(); return title;}
	public String getArtist(){return artist;}
	public String getAlbum(){return "";}
	public String getTrack(){return "";}
	public String getLyrics(){return "";}
	public String getGenre(){return "";}
	public String toString(){return new File(res).getName();}

	public String formatName(){return "Native FLAC";}
	public String[] getExtensions(){return new String[]{".flac"};}	
}
